#include <math.h>
#include "Vertex3f.h"

#define SQR(x) (x*x)
#define Tolerance 1e-5f

Vertex3f::Vertex3f(float X, float Y, float Z)
{
	this->X = X;
	this->Y = Y;
	this->Z = Z;
	this->neighbors = 0;
	this->Color = 0;
	this->IsColored = false;
}

Vertex3f::Vertex3f(Vertex3f *vertex)
{
	this->X = vertex->X;
	this->Y = vertex->Y;
	this->Z = vertex->Z;
	this->neighbors = 0;
	this->Color = 0;
	this->IsColored = false;
}

Vertex3f* Vertex3f::GetNearestNeighbor(Vertex3f *targetVertex)
{
	Vertex3f *nearest = this->neighbors->at(0);
	float minimumdistance = this->neighbors->at(0)->GetEuclideanDistance(targetVertex);
	float distance;

	for(int i=1; i< this->neighbors->count(); i++)
	{
		distance = this->neighbors->at(i)->GetEuclideanDistance(targetVertex);
		if(distance < minimumdistance)
		{
			minimumdistance = distance;
			nearest = this->neighbors->at(i);
		}
	}
	return nearest;
}
#pragma region ColoringRendering
void Vertex3f::SetColor(unsigned char r, unsigned char g, unsigned char b, unsigned char a)
{
	Color->R = r;
	Color->G = g;
	Color->B = b;
	Color->A = a;
}

void Vertex3f::SetColor(Color4b *color)
{
	Color->R = color->R;
	Color->G = color->G;
	Color->B = color->B;
	Color->A = color->A;
}

void Vertex3f::RenderVertexColorCheck(Color4b *color)
{
	if(IsColored != 0)
		glColor4ub(Color->R, Color->G, Color->B, Color->A);
	else glColor4ub(color->R, color->G, color->B, color->A);
	glVertex3d(X, Y, Z);
}

void Vertex3f::RenderVertexNoColorCheck(Color4b *color)
{
	glColor4ub(color->R, color->G, color->B, color->A);
	glVertex3d(X, Y, Z);
}
#pragma endregion ColoringRendering



#pragma region MathFunctions

float Vertex3f::GetLength() const
{
	return (float)(sqrt(SQR(this->X) + SQR(this->Y) + SQR(this->Z)));
}

float Vertex3f::GetEuclideanDistance(const Vertex3f *vertex) const
{
	float xDiff = this->X;
	float yDiff = this->Y;
	float zDiff = this->Z;
	xDiff -= vertex->X;
	yDiff -= vertex->Y;
	zDiff -= vertex->Z;
	return sqrt( SQR(xDiff) + SQR(yDiff)+ SQR(zDiff));
}

float Vertex3f::GetProjectionLength(Vertex3f *directionVectorStart, Vertex3f *directionVector) const
{
	Vertex3f *VectorU = directionVector;
	Vertex3f *VectorV = this->Difference(directionVectorStart);

	float UCoeff= VectorU->DotProduct(VectorV) / (SQR(VectorU->X) + SQR(VectorU->Y) + SQR(VectorU->Z));

	Vertex3f VectorResolute(UCoeff * VectorU->X, UCoeff * VectorU->Y, UCoeff * VectorU->Z);

	Vertex3f PointofProjection(
		directionVectorStart->X + VectorResolute.X,
		directionVectorStart->Y + VectorResolute.Y,
		directionVectorStart->Z + VectorResolute.Z
		);

	delete VectorV;
	return PointofProjection.GetEuclideanDistance(this);
}

void Vertex3f::Normalize()
{
	float length = this->GetLength();
	if (length == 0.0f) 
	{
		this->X = 0;
		this->Y = 0;
		this->Z = 0;
	}
	else
	{
		this->X /= length;
		this->Y /= length;
		this->Z /= length;
	}
}

Vertex3f* Vertex3f::Sum(Vertex3f *u) const
{
	return new Vertex3f(this->X + u->X, this->Y + u->Y, this->Z + u->Z);
}

Vertex3f* Vertex3f::Difference (Vertex3f *u) const
{
	return new Vertex3f(this->X - u->X, this->Y - u->Y, this->Z - u->Z);
}

void Vertex3f::Add(Vertex3f *u)
{
	this->X += u->X;
	this->Y += u->Y;
	this->Z += u->Z;
}

void Vertex3f::Subtract(Vertex3f *u)
{
	this->X -= u->X;
	this->Y -= u->Y;
	this->Z -= u->Z;
}

Vertex3f* Vertex3f::Multpli(float value) const
{
	return new Vertex3f(this->X * value, this->Y * value, this->Z * value);
}

Vertex3f* Vertex3f::CrossProduct(Vertex3f * u) const
{
	float newX = this->Y * u->Z;
	newX -= this->Z * u->Y;
	float newY = this->Z * u->X;
	newY -= this->X * u->Z;
	float newZ = this->X * u->Y;
	newZ -= this->Y * u->X;
	return new Vertex3f(newX, newY, newZ);
}

float Vertex3f::DotProduct(Vertex3f *u) const
{
	return this->X * u->X + this->Y * u->Y + this->Z * u->Z;
}

#pragma endregion MathFunctions

Vertex3f::~Vertex3f()
{
	if(neighbors != 0)
		delete neighbors;
	if(Color != 0)
	{
		delete Color;
		Color = 0;
	}
}
